#include "timer.h"

#include "shared.h"

// Possibly more accurate than U++ TimeStop which is POSIX
// Implement as a singleton so we can time across disjoint sections that don't share the timer scope
// Update: Singleton causes heap error and stack overflow
// Behaves differently than TimeStop in that calling the Elapse functions defaults to reseting the timer

Timer::Timer() {
	QueryPerformanceCounter(&largestarttime);
	starttime = largestarttime.QuadPart;
	LARGE_INTEGER largeticksPerSecond;
	QueryPerformanceFrequency(&largeticksPerSecond);
	ticksPerSecond = largeticksPerSecond.QuadPart;
	withDecimals = false;
}

void Timer::Reset() {
	QueryPerformanceCounter(&largestarttime);
	starttime = largestarttime.QuadPart;
}

long long Timer::Elapsed() const {
	LARGE_INTEGER tempstarttime;
	QueryPerformanceCounter(&tempstarttime);
	return tempstarttime.QuadPart - starttime;
}

long long Timer::ElapsedDays() const {
	return ConvertTicksToDays(Elapsed());
}

long long Timer::ElapsedHours() const {
	return ConvertTicksToHours(Elapsed());
}

long long Timer::ElapsedMinutes() const {
	return ConvertTicksToMinutes(Elapsed());
}

long long Timer::ElapsedSeconds() const {
	return ConvertTicksToSeconds(Elapsed());
}

long long Timer::ElapsedMilliseconds() const {
	return ConvertTicksToMilliseconds(Elapsed());
}

long long Timer::ElapsedMicroseconds() const {
	return ConvertTicksToMicroseconds(Elapsed());
}

long long Timer::ConvertTicksToDays(long long ticks, bool remainder) const {
	return remainder?
	  ticks % (ticksPerSecond * 60 * 60 * 24) :
	  ticks / (ticksPerSecond * 60 * 60 * 24);
}

long long Timer::ConvertTicksToHours(long long ticks, bool remainder) const {
	return remainder?
	  ticks % (ticksPerSecond * 60 * 60) :
	  ticks / (ticksPerSecond * 60 * 60);
}

long long Timer::ConvertTicksToMinutes(long long ticks, bool remainder) const {
	return remainder?
	  ticks % (ticksPerSecond * 60) :
	  ticks / (ticksPerSecond * 60);
}

long long Timer::ConvertTicksToSeconds(long long ticks, bool remainder) const {
	return remainder?
	  ticks % (ticksPerSecond) :
	  ticks / (ticksPerSecond);
}

long long Timer::ConvertTicksToMilliseconds(long long ticks, bool remainder) const {
	return remainder?
	  ticks % (ticksPerSecond / THOUSAND) :
	  ticks / (ticksPerSecond / THOUSAND);
}

long long Timer::ConvertTicksToMicroseconds(long long ticks, bool remainder) const {
	return remainder?
	  ticks % (ticksPerSecond / MILLION) :
	  ticks / (ticksPerSecond / MILLION);
}

// Smart display for humans

String Timer::ToString() const {
	long long ticks = Elapsed();
	
	if (ticks < TENTHOUSAND) {
		return Format("%d ticks (%d/sec)", ticks, ticksPerSecond);
	} else if (ConvertTicksToMicroseconds(ticks) < TENTHOUSAND) {
		return Format("%d µs", ConvertTicksToMicroseconds(ticks), ConvertTicksToMicroseconds(ticks, true));
	} else if (ConvertTicksToMilliseconds(ticks) < TENTHOUSAND) {
		if (withDecimals)
			return Format("%d.%03d ms", ConvertTicksToMilliseconds(ticks), ConvertTicksToMilliseconds(ticks, true));
		else
			return Format("%d ms", ConvertTicksToMilliseconds(ticks));
	} else if (ConvertTicksToSeconds(ticks) < 60) {
		return Format("%d.%03d s", ConvertTicksToSeconds(ticks), ConvertTicksToSeconds(ticks, true));
	} else if (ConvertTicksToMinutes(ticks) < 60) {
		return Format("%d.%03d m", ConvertTicksToMinutes(ticks), ConvertTicksToMinutes(ticks, true));
	} else if (ConvertTicksToHours(ticks) < 24) {
		return Format("%d.%03d h", ConvertTicksToHours(ticks), ConvertTicksToHours(ticks, true));
	} else {
		return Format("%d.%03d d", ConvertTicksToDays(ticks), ConvertTicksToDays(ticks, true));
	}
}

